import geom.Vector;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;

import processing.core.*;



public class Edge{
	public static int nextID = 0;
	int id = 0;
	
	public static final int TOP = 0;
	public static final int BTM = 1;
	public static final int LEFT = 2;
	public static final int RIGHT = 3;
	
	public static final int HORIZONTAL = 0;
	public static final int VERTICAL = 1;
	public static final int LOW = 0;
	public static final int HIGH = 1;
	public int dir;
	public int hilo;
	
	public static float EPSILON = 0.01f;
	Vector pos;
	float l;
	RectNode parent;
	ArrayList anchors;
	public int index;
	
	public Edge(Rectangle nr, int s){
		switch(s){
		case TOP: 
			dir = HORIZONTAL;
			hilo = LOW;
			break;
		case BTM:
			dir = HORIZONTAL;
			hilo = HIGH;
			break;
		case LEFT: 
			dir = VERTICAL;
			hilo = LOW;
			break;
		case RIGHT:
			dir = VERTICAL;
			hilo = HIGH;
			break;
		}
		pos = new Vector(nr.x + dir * hilo * nr.w, nr.y + (1-dir) * hilo * nr.h, 0);
		if(dir == HORIZONTAL) l = nr.w;
		else l = nr.h;
		anchors = new ArrayList();
		if(nr instanceof RectNode)
			parent = (RectNode)nr;
		setID();
	}
	
	public Edge(RectNode nr, int d, int p){
		dir = d;
		hilo = p;
		pos = new Vector(nr.x + dir * nr.w / 2f, nr.y + hilo * nr.h / 2f, 0  );
		if(dir == HORIZONTAL) l = nr.w;
		else l = nr.h;
		anchors = new ArrayList();
		setID();
	}
	
	public Edge(Vector v, float l, int d, int p){
		dir = d;
		hilo = p;
		pos = v;
		this.l = l;
		anchors = new ArrayList();
		setID();
	}
	
	public Edge(float x, float y, float l, int d, int p){
		dir = d;
		hilo = p;
		pos = new Vector(x, y, 0);
		this.l = l;
		anchors = new ArrayList();
		setID();
	}
	
	public void clear(){
		parent.removeEdge(this);
	}
	
	public void add(RectNode rn){
		rn.addEdge(this);
	}
	
	public void mergeAnchors(Edge e2){
		Iterator i1 = anchors.iterator();
		while(i1.hasNext()){
			Anchor a1 = (Anchor) i1.next();
			Iterator i2 = e2.anchors.iterator();
			while(i2.hasNext()){
				Anchor a2 = (Anchor)i2.next();
				if(a1.id != a2.id) 
					addAnchor(a2);
			}
		}
	}
	
	public float getParVal(){
		if(dir == HORIZONTAL) return pos.x;
		else return pos.y;
	}
	public float getPerpVal(){
		if(dir == HORIZONTAL) return pos.y;
		else return pos.x;
	}
	
	public void setParVal(float v){
		if(dir == HORIZONTAL) pos.x = v;
		else pos.y = v;
	}
	public void setPerpVal(float v){
		if(dir == HORIZONTAL) pos.y = v;
		else pos.x = v;
	} 
	
	public float getParDistance(Edge e){
		if(dir == e.dir){
			return getPerpVal() - e.getPerpVal();
		}
		return Float.NaN;
	}
	
	public Vector getEndPos(){
		if(dir == HORIZONTAL) return new Vector(pos.x + l, pos.y, 0);
		else return new Vector(pos.x, pos.y + l, 0);
	}
	
	public int getSide(){
		if(dir == HORIZONTAL && hilo == LOW) return TOP;
		else if(dir == HORIZONTAL && hilo == HIGH) return BTM;
		else if(dir == VERTICAL && hilo == LOW) return LEFT;
		else return RIGHT;
	}

	public boolean intersects(Edge e){
		if(e.dir != dir){
			if(dir == HORIZONTAL){
				if(e.pos.y < pos.y)
					if(e.pos.y + e.l > pos.y)
						return true;
			}
			else 
				if(e.pos.x < pos.x)
					if(e.pos.x + e.l > pos.x)
						return true;
		}
		return false;
	}




	public void addAnchor(int arg0, Anchor arg1) {
		anchors.add(arg0, arg1);
	}

	public void addAnchor(Anchor arg0) {
		anchors.add(arg0);
	}

	public void clearAnchors() {
		anchors.clear();
	}

	public void removeAnchor(int arg0) {
		anchors.remove(arg0);
	}
	public static final int NOT_INSIDE = -1;
	public static final int PART_INSIDE = 0;
	public static final int INSIDE = 1;
	public int isInside(Rectangle r){
		if(r.contains(pos)){
			if(r.contains(getEndPos()))
				return INSIDE;
			else return PART_INSIDE;
		}
		else if(r.contains(getEndPos())){
			return PART_INSIDE;
		}
		else return NOT_INSIDE;
	}
	
	public void removeAnchor(Anchor a){
		ListIterator i = anchors.listIterator();
		boolean removed = false;
		while(i.hasNext() && !removed){
			Anchor a0 = (Anchor)i.next();
			if(a0.id == a.id){
				removed = true;
				i.remove();
			}
		}
	}

	public int anchorSize() {
		return anchors.size();
	}
	
	public Anchor getAnchor(int i){
		return (Anchor)anchors.get(i);
	}
	
	public boolean equalPerp(Edge e){
		if(dir != e.dir) return false;
		if(getPerpVal() == e.getPerpVal()) return true;
		return false;
	}

	public String getOverlapString(int i){
		switch(i){
		case NO_OVERLAP: return "no overlap";
		case TOUCH_BEG: return "touch beg.";
		case OVERLAP_BEG: return "overlap beg.";
		case ENCOMPASSES: return "encompasses";
		case ENCOMPASSED: return "encompassed";
		case EQUAL: return "equal";
		case OVERLAP_END: return "overlap end";
		case TOUCH_END: return "touch end";
		default: return "undef";
		}
	}
	
	public static final int NO_OVERLAP = -1000;
	public static final int TOUCH_BEG = -2;
	public static final int OVERLAP_BEG = -1;
	public static final int ENCOMPASSES =-10;
	public static final int EQUAL = -100;
	public static final int ENCOMPASSED = 10;
	public static final int OVERLAP_END = 1;
	public static final int TOUCH_END = 2;
	
	
	public int overlap(Edge e){
		float es1 = getParVal();
		float ee1 = es1 + l;
		float es2 = e.getParVal();
		float ee2 = es2 + e.l;
		if(es2 < es1){
			if(hilo != e.hilo){
				if(ee2 < es1) return NO_OVERLAP;
				if(ee2 >= ee1) return ENCOMPASSED;
				if(ee2 > es1) return OVERLAP_BEG;
			}
			else
				if(ee2 == es1) return TOUCH_BEG;
		}
		if(ee2 > ee1){
			if(hilo != e.hilo){
				if(es2 > ee1) return NO_OVERLAP;
				if(es2 <= es1) return ENCOMPASSED;
				if(es2 < ee1) return OVERLAP_END;
			}
			else
				if(es2 == ee1) return TOUCH_END;
		}
		if(hilo != e.hilo){
			if(es2 == es1 && ee1 == ee2) return EQUAL;
			if(es2 >= es1 && ee2 <= ee1) return ENCOMPASSES;
		}
		return NO_OVERLAP;
	}
	
	public void draw(PApplet pa, PFont font){
		pa.stroke(250, 20, 10, 100);
		//pa.ellipse(pos.x, pos.y, 4, 4);
		if(dir == HORIZONTAL){
			pa.line(pos.x, pos.y, pos.x + l, pos.y);
			float n = Math.max(l / 8f, 4);
			if(hilo == LOW) n *= -1f;
			pa.line(pos.x + l / 2f, pos.y, pos.x + l / 2f,  pos.y + n);
		}
		else{
			pa.line(pos.x, pos.y, pos.x, pos.y + l);
			float n = Math.min(l / 8f, 4);
			if(hilo == LOW) n *= -1f;
			pa.line(pos.x, pos.y + l / 2f, pos.x + n,  pos.y + l / 2f);
		}
		//Iterator a = anchors.iterator();
		//pa.fill(250, 180, 30, 90);
		//while(a.hasNext()){
		//	((Anchor)a.next()).draw(pa, font);
		//}
	}
	
	public String toString(){
		StringBuffer sb = new StringBuffer();
		sb.append("Edge: pos:");
		sb.append(pos.toString());
		sb.append(" Length: ");
		sb.append(l);
		sb.append(" dir: ");
		sb.append(dir);
		sb.append(" hilo ");
		sb.append(hilo);
		return sb.toString();
	}
	
	public void move(float x, float y){
		pos.x += x;
		pos.y += y;
	}
	
	public boolean onEdge(Vector a){
		if(a.x >= pos.x){
			if(a.y >= pos.y){
				if(dir == HORIZONTAL){
					if(a.x <= pos.x + l) return true;
				}
				else if(a.y <= pos.y + l) return true;
			}
		}
		return false;
	}
	
	public void setID(){
		id = nextID;
		if(nextID > 128000)
			nextID = 0;
	}
}
